﻿using System;

namespace Syngine.Content
{
	/// <summary>
	/// 
	/// </summary>
	public abstract class Asset : IDisposable
	{
		protected Asset(string assetName)
		{
			if (assetName == null)
			{
				throw new ArgumentNullException("assetName");
			}

			AssetName = assetName;
		}

		/// <summary>
		/// Gets or sets the name of the asset.
		/// </summary>
		/// <value>
		/// The name of the asset.
		/// </value>
		public string AssetName { get; protected set; }

		/// <summary>
		/// Creates the specified asset name.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="assetName">Name of the asset.</param>
		/// <returns></returns>
		public static Asset<T> Create<T>(string assetName) where T : class
		{
			return new Asset<T>(assetName);
		}

		/// <summary>
		/// Loads the content.
		/// </summary>
		public abstract void LoadContent();

		/// <summary>
		/// Loads the content.
		/// </summary>
		/// <param name="context">The context.</param>
		public abstract void LoadContent(LoadContext context);

		public abstract void Dispose();
	}

	/// <summary>
	/// 
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public class Asset<T> : Asset where T : class
	{
		private T _resource;

		/// <summary>
		/// Initializes a new instance of the <see cref="Asset{T}"/> class.
		/// </summary>
		/// <param name="assetName">Name of the asset.</param>
		public Asset(string assetName) : base(assetName)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="Asset{T}"/> class.
		/// </summary>
		/// <param name="assetName">Name of the asset.</param>
		/// <param name="asset"></param>
		public Asset(string assetName, T asset) : base(assetName)
		{
			_resource = asset;
		}

		/// <summary>
		/// Gets or sets the typed asset.
		/// </summary>
		/// <value>The typed asset.</value>
		public T Resource
		{
			get
			{
				if (_resource == null)
				{
					LoadContent();
				}

				return _resource;
			}
		}

		/// <summary>
		/// Returns the specified assets resource.
		/// </summary>
		/// <param name="asset">The asset.</param>
		/// <returns></returns>
		public static implicit operator T(Asset<T> asset)
		{
			if (asset == null)
			{
				throw new ArgumentNullException("asset");
			}

			return asset.Resource;
		}

		/// <summary>
		/// Returns the specified assets resource.
		/// </summary>
		/// <param name="asset">The asset.</param>
		/// <returns></returns>
		public static implicit operator Asset<T>(T asset)
		{
			if (asset == null)
			{
				throw new ArgumentNullException("asset");
			}

			return new Asset<T>(string.Empty, asset);
		}

		/// <summary>
		/// Loads the content.
		/// </summary>
		public override void LoadContent()
		{
			LoadContent(GameContext.CreateLoadContext());
		}

		/// <summary>
		/// Loads the content.
		/// </summary>
		/// <param name="context">The context.</param>
		public override void LoadContent(LoadContext context)
		{
			if (_resource == null)
			{
				_resource = context.Load<T>(AssetName);
			}
		}

		public override void Dispose()
		{
			var disposable = Resource as IDisposable;

			if (disposable != null)
			{
				disposable.Dispose();
			}
		}
	}
}